package sanshui.system.trade.factory.distribute;

import cn.hutool.core.bean.BeanUtil;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import sanshui.system.trade.config.SpringContextHolder;
import sanshui.system.trade.db.entity.*;
import sanshui.system.trade.db.mapper.SelfTradeReportMapper;
import sanshui.system.trade.db.mapper.TradeAccountMapper;
import sanshui.system.trade.db.mapper.TradeHisOpPosMapper;
import sanshui.system.trade.db.mapper.TradeReportMapper;
import sanshui.system.trade.model.TradeAccountModel;
import sanshui.system.trade.model.TradeHisPositionModel;
import sanshui.system.trade.model.TradeReportModel;
import sanshui.system.trade.service.IAccountService;
import sanshui.system.trade.service.ITradeReportService;
import sanshui.system.trade.util.CommonTimeUtil;

import java.util.*;
import java.util.concurrent.*;

@Data
@Slf4j
public class DistributeManager {
    public enum OpType {
        UPDATE, INSERT
    }

    boolean isRun = true;
    boolean isBusyPosition = false;
    boolean isBusyAcc = false;
    boolean isBusyReport = false;

    IAccountService accountService = SpringContextHolder.getBean(IAccountService.class);
    TradeHisOpPosMapper positionMapper = SpringContextHolder.getBean(TradeHisOpPosMapper.class);
    TradeAccountMapper accountTblMapper = SpringContextHolder.getBean(TradeAccountMapper.class);
    ITradeReportService reportService = SpringContextHolder.getBean(ITradeReportService.class);
    TradeReportMapper reportMapper = SpringContextHolder.getBean(TradeReportMapper.class);
    private Deque<TradeHisPositionModel> distributeQueuePosition;
    private Deque<TradeAccountModel> distributeQueueAccount;
    private Deque<TradeReportModel> distributeQueueReport;

    ExecutorService threadPool;
    ExecutorCompletionService<Boolean> servicePosition;
    ExecutorCompletionService<Boolean> serviceAccount;
    ExecutorCompletionService<Boolean> serviceReport;

    List<Future<Boolean>> executorResultList = new ArrayList<>();
    List<TradeReport> batchInsertListReport = new ArrayList<>();

    private final static String TYPE_POSITION = "1";
    private final static String TYPE_ACCOUNT = "2";
    private final static String TYPE_REPORT = "3";

    public DistributeManager() {
        distributeQueuePosition = new ArrayDeque<>();
        distributeQueueAccount = new ArrayDeque<>();
        distributeQueueReport = new ArrayDeque<>();

        threadPool = Executors.newFixedThreadPool(5);
        servicePosition = new ExecutorCompletionService<>(threadPool);
        serviceAccount = new ExecutorCompletionService<>(threadPool);
        serviceReport = new ExecutorCompletionService<>(threadPool);

        executorResultList.add(servicePosition.submit(call(TYPE_POSITION)));
        executorResultList.add(serviceAccount.submit(call(TYPE_ACCOUNT)));
        executorResultList.add(serviceReport.submit(call(TYPE_REPORT)));
    }

    private Callable<Boolean> call(String type) {
        return () -> {
            while (isRun) {
                switch (type){
                    case TYPE_POSITION:
                        // 持仓
                        TradeHisPositionModel positionModel = distributeQueuePosition.poll();
                        if (positionModel != null) {
                            if (OpType.UPDATE.compareTo(positionModel.getDisOpType()) == 0) {
                                this.dealWithPositionAndUpdate(positionModel);
                            } else {
                                this.dealWithPositionAndInsert(positionModel);
                            }
                        };break;
                    case TYPE_ACCOUNT:
                        // 账户
                        TradeAccountModel accountModel = distributeQueueAccount.poll();
                        if (accountModel != null) {
                            this.dealWithAccountAndUpdate(accountModel);
                        };break;
                    case TYPE_REPORT:
                        // 报告
                        TradeReportModel reportModel = distributeQueueReport.poll();
                        if (reportModel != null) {
                            this.dealWithReportAndInsert(reportModel);
                        };break;
                }
                if (this.isEmpty()){
                    CommonTimeUtil.sleep(1000L);
                }
            }
            return true;
        };
    }

    private void dealWithPositionAndUpdate(TradeHisPositionModel positionModel) {
        TradeHisOpPos tradeHisPosition = new TradeHisOpPos();
        BeanUtil.copyProperties(positionModel, tradeHisPosition);

        TradeHisOpPosExample tradeHisPositionExample = new TradeHisOpPosExample();
        TradeHisOpPosExample.Criteria criteria = tradeHisPositionExample.createCriteria();
        criteria.andOpIdEqualTo(tradeHisPosition.getOpId());
        positionMapper.updateByExampleSelective(tradeHisPosition, tradeHisPositionExample);
    }

    private void dealWithPositionAndInsert(TradeHisPositionModel positionModel) {
        TradeHisOpPos tradeHisPosition = new TradeHisOpPos();
        BeanUtil.copyProperties(positionModel, tradeHisPosition);
        positionMapper.insertSelective(tradeHisPosition);
    }

    private void dealWithReportAndInsert(TradeReportModel reportModel) {
        TradeReport tradeReport = new TradeReport();
        BeanUtil.copyProperties(reportModel, tradeReport);
        reportMapper.insertSelective(tradeReport);
//        if (batchInsertListReport.size() < 10){
//            batchInsertListReport.add(tradeReport);
//            return;
//        }
//        this.batchInserReport();
    }

    private int batchInserReport(){
        int rows = reportService.saveReportBatch(batchInsertListReport);
        batchInsertListReport.clear();
        return rows;
    }

    private void dealWithAccountAndUpdate(TradeAccountModel accountModel) {
        TradeAccount accountTbl = new TradeAccount();
        BeanUtil.copyProperties(accountModel, accountTbl);

        TradeAccountExample updateAccExm = new TradeAccountExample();
        TradeAccountExample.Criteria updateAccCri = updateAccExm.createCriteria();
        updateAccCri.andAccIdEqualTo(accountTbl.getAccId());
        accountTblMapper.updateByExampleSelective(accountTbl, updateAccExm);
    }

    public void addPosition(TradeHisPositionModel positionModel) {
        distributeQueuePosition.add(positionModel);
        isBusyPosition = false;
        if (distributeQueuePosition.size() > 1000) {
            log.info("分发仓位队列>1000，超负荷");
            isBusyPosition = true;
        }
    }

    public void addReport(TradeReportModel reportModel) {
        distributeQueueReport.add(reportModel);
        isBusyReport = false;
        if (distributeQueueReport.size() > 1000) {
            log.info("分发报告队列>1000，超负荷");
            isBusyReport = true;
        }
    }

    public void addAccount(TradeAccountModel account) {
        distributeQueueAccount.add(account);
        isBusyAcc = false;
        if (distributeQueueAccount.size() > 1000) {
            log.info("分发账户队列>1000，超负荷");
            isBusyAcc = true;
        }
    }

    public boolean isBusy() {
        return isBusyPosition || isBusyAcc || isBusyReport;
    }

    public void close() {
        log.info("关闭前做批量提交");
//        this.batchInserReport();
        isRun = false;
        log.info("批量提交--完成");
    }

    public boolean isEmpty() {
        if (!distributeQueuePosition.isEmpty()) log.debug("仓位队列不为空，剩余SIZE:{}", distributeQueuePosition.size());
        if (!distributeQueueAccount.isEmpty()) log.debug("仓位队列不为空，剩余SIZE:{}", distributeQueueAccount.size());
        if (!distributeQueueReport.isEmpty()) log.debug("仓位队列不为空，剩余SIZE:{}", distributeQueueReport.size());
        return distributeQueuePosition.isEmpty() && distributeQueueAccount.isEmpty() && distributeQueueReport.isEmpty();
    }
}
